<div id="app">
  {{title}}
  <comp></comp>
</div>

<script>
  // 1.基本结构
  const Vue = {
    // 扩展性
    // 平台无关性
    createRenderer({ querySelector, insert }) {
      return {
        createApp(options) {
          return {
            mount(selector) {
              const parent = querySelector(selector);
              if (!options.render) {
                options.render = this.compile(parent.innerHTML);
              }
              if (options.setup) {
                this.setupState = options.setup();
              }
              if (options.data) {
                this.data = options.data();
              }
              const proxyData = new Proxy(this, {
                get(target, key) {
                  if (target.setupState && key in target.setupState) {
                    return target.setupState[key];
                  }
                  return target.data[key];
                },
                set(target, key, val) {
                  if (target.setupState && key in target.setupState) {
                    target.setupState[key] = val;
                  } else {
                    target.data[key] = val;
                  }
                },
              });
              // 将update 变为副作用函数 effect()
              //  放入依赖
              this.update = effect(()=> {
                const el = options.render.call(proxyData);
                // 3.追加到宿主
                parent.innerHTML = "";
                insert(el, parent)
              })
            },
            compile(template) {
              return function render() {
                const h3 = document.createElement("h3");
                h3.textContent = this.title;
                return h3;
              };
            }
          };
        },
      };
    },

    createApp(options) {
      // 创建一个web平台特有渲染器
      const renderer = Vue.createRenderer({
        querySelector(sel) {
          return document.querySelector(sel);
        },
        insert(el, parent) {
          parent.appendChild(el);
        },
      });
      return renderer.createApp(options);
    },
  };
</script>
<script>
  // 数据拦截访问 => 响应式
  function reactive(obj) {
    return new Proxy(obj, {
      get(target, key){
        console.log('get key:', key)
        // 收集依赖
        track(target, key)
        return target[key]
      },
      set(target, key, val){
        console.log('set key:', key)
        target[key] = val
        // 触发依赖
        trigger(target, key)
        // app.update()
      }
    })
  }
  // 依赖收集
  // 建立映射关系: 依赖dep - 组件更新函数
  // vue2 : 每个组件 => watcher
  // vue3 : Map() 
  
  // { target: {key: [update1, update2]} }
  const targetMap = {}
  // 源码 ===> weakMap()  弱键 
  // 与Map相比好处 => 性能更优、防止内存泄漏、垃圾回收

  // 调用effect, 首先执行fn
  const effectStack = []
  function effect(fn) {
    const eff = function() {
      try {
        effectStack.push(eff)
        fn()
      } finally {
        effectStack.pop()
      }
    }
    eff()
    return eff
  }
  // 建立target, key 和 effectStack 中副作用函数之间的关系
  function track(target, key) {
    const effect = effectStack[effectStack.length-1]
    targetMap[target] = targetMap[target] || {}
    let deps = targetMap[target][key]
    if(!deps){
      deps = targetMap[target][key] = []
    }
    // 映射关系建立
    if(deps.indexOf(effect) === -1){
      deps.push(effect)
    }
  }

  function trigger(target, key) {
    const map = targetMap[target]
    if(map && map[key]){
      map[key].forEach(dep => {
        dep()
      })
    }
  }
  // test code
  // const obj = reactive({
  //   foo:'foo'
  // })
  // effect(() => {
  //   console.log(obj.foo)
  // })
  // obj.foo = 'bar'

  const app = Vue.createApp({
    setup() {
      const state = reactive({
        title: 'vue3, hello!'
      })
      setTimeout(() => {
        state.title = '2s后 hello, vue3!'
      },2000)
      return state
    },
  });

  app.mount("#app");
</script>
